﻿using System;
using System.Runtime.InteropServices;
using System.Windows.Interop;
using System.Windows.Media;

namespace Aeon.Presentation
{
    /// <summary>
    /// Simplifies creation and management of an InteropBitmap.
    /// </summary>
    internal sealed class FastBitmap : IDisposable
    {
        #region Constructors
        /// <summary>
        /// Initializes a new instance of the FastBitmap class.
        /// </summary>
        /// <param name="width">Width of the bitmap in pixels.</param>
        /// <param name="height">Height of the bitmap in pixels.</param>
        public FastBitmap(int width, int height)
        {
            CreateInteropBitmap(width, height);
        }
        ~FastBitmap()
        {
            Dispose(false);
        }
        #endregion

        #region Public Properties
        /// <summary>
        /// Gets the InteropBitmap instance.
        /// </summary>
        public InteropBitmap InteropBitmap
        {
            get { return graphicsBitmap; }
        }
        /// <summary>
        /// Gets the pointer to the bitmap pixel data.
        /// </summary>
        public IntPtr PixelBuffer
        {
            get { return pixelBuffer; }
        }
        #endregion

        #region Public Methods
        /// <summary>
        /// Releases unmanaged resources used by the bitmap.
        /// </summary>
        public void Dispose()
        {
            Dispose(true);
        }
        /// <summary>
        /// Forces a redraw of the InteropBitmap.
        /// </summary>
        public void Invalidate()
        {
            graphicsBitmap.Invalidate();
        }
        #endregion

        #region Private Methods
        /// <summary>
        /// Releases unmanaged resources used by the bitmp.
        /// </summary>
        /// <param name="disposing">Value indicating whether the method is called from the Dispose method.</param>
        private void Dispose(bool disposing)
        {
            if(!disposed)
            {
                if(pixelBuffer != IntPtr.Zero)
                {
                    UnmapViewOfFile(pixelBuffer);
                    pixelBuffer = IntPtr.Zero;
                }
                if(section != IntPtr.Zero)
                {
                    CloseHandle(section);
                    section = IntPtr.Zero;
                }
                disposed = true;
                if(disposing)
                    GC.SuppressFinalize(this);
            }
        }
        /// <summary>
        /// Allocate the memory required by the bitmap.
        /// </summary>
        /// <param name="pixelWidth">Width of the bitmap in pixels.</param>
        /// <param name="pixelHeight">Height of the bitmap in pixels.</param>
        private void CreateInteropBitmap(int pixelWidth, int pixelHeight)
        {
            int byteSize = pixelWidth * pixelHeight * 4;
            section = CreateFileMapping(INVALID_HANDLE_VALUE, IntPtr.Zero, PAGE_READWRITE, 0, (uint)byteSize + 4096u, null);
            pixelBuffer = MapViewOfFile(section, FILE_MAP_ALL_ACCESS, 0, 0, (uint)byteSize);
            graphicsBitmap = (InteropBitmap)Imaging.CreateBitmapSourceFromMemorySection(section, pixelWidth, pixelHeight, PixelFormats.Bgr32, pixelWidth * 4, 0);
        }
        #endregion

        #region Private Fields
        private InteropBitmap graphicsBitmap;
        private IntPtr section;
        private IntPtr pixelBuffer;
        private bool disposed;
        #endregion

        #region P/Invoke
        [DllImport("kernel32.dll", SetLastError = true)]
        private static extern IntPtr CreateFileMapping(IntPtr hFile, IntPtr lpFileMappingAttributes, uint flProtect, uint dwMaximumSizeHigh, uint dwMaximumSizeLow, string lpName);
        [DllImport("kernel32.dll", SetLastError = true)]
        private static extern IntPtr MapViewOfFile(IntPtr hFileMappingObject, uint dwDesiredAccess, uint dwFileOffsetHigh, uint dwFileOffsetLow, uint dwNumberOfBytesToMap);
        [DllImport("kernel32.dll", SetLastError = true)]
        private static extern uint UnmapViewOfFile(IntPtr lpBaseAddress);
        [DllImport("kernel32.dll", SetLastError = true)]
        private static extern uint CloseHandle(IntPtr hObject);

        private const uint FILE_MAP_ALL_ACCESS = 0xF001F;
        private const uint PAGE_READWRITE = 0x04;
        private static readonly IntPtr INVALID_HANDLE_VALUE = new IntPtr(-1);
        #endregion
    }
}
